package com.duoduo.commonn.redis.factory;


import com.duoduo.common.api.constants.Constant;
import com.duoduo.common.api.constants.LogConstant;
import com.duoduo.common.api.domain.DelayExecuteMessage;
import com.duoduo.common.api.exception.lock.AddLockException;
import com.duoduo.common.api.exception.lock.ReleaseLockException;
import com.duoduo.common.api.exception.lock.RerentLockException;
import com.duoduo.common.api.exception.lock.UnrerentLockException;
import com.duoduo.common.api.help.*;
import com.duoduo.common.api.myinterface.LockOperator;
import com.duoduo.common.api.myinterface.tracer.RedisTracer;
import com.duoduo.common.api.spi.extension.ExtensionLoader;
import com.duoduo.common.api.tuple.Tuple;
import com.duoduo.common.api.tuple.TwoTuple;
import com.duoduo.commonn.redis.LogThreadLocal;
import com.duoduo.commonn.redis.handle.TraceHandler;
import com.duoduo.commonn.redis.myinterface.RedisOperator;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.util.*;

public class RedisLockFactory implements LockOperator {

	private static final Logger logger = LoggerFactory.getLogger(RedisLockFactory.class);
	private static final Long TRY_FALSE = -1L;
	private static final int RELEASE_FALSE = -1;
	private static final int RELEASE_SUCCESS = 0;
	private static final int EXPIRE_TIME = 16; // 锁的有效期
	private static final int MAX_RERENT_TIMES = 5; // 最大续租次数
	private static final int DELAY_TIME = EXPIRE_TIME >> 1; // 锁的续租时间间隔
	private RedisOperator redisOperator;
	//每一个请求的标识
	private static ThreadLocal<RequestIdData> requestIdThreadLocal = new ThreadLocal<RequestIdData>() {
		public RequestIdData initialValue() {
			return new RequestIdData();
		}
	};
	private static ThreadLocal<DelayTaskData> threadIdThreadLocal = new ThreadLocal<DelayTaskData>() {
		public DelayTaskData initialValue() {
			return new DelayTaskData();
		}
	};

	public RedisLockFactory(){
		try{
			//分布式锁默认使用redis实现，redis默认使用jedis操作
			String redisImplType = SpringContextInnerUtil.get(Constant.REDIS_IMPL);
			logger.info(LogConstant.REDIS_CACHE + " impl type: {}", redisImplType);
			// 如果有链路跟踪
	     	if(SpringContextInnerUtil.containsBean(Constant.TRACER_BEANNAME)){
				RedisOperator proxy = ExtensionLoader.getExtensionLoader(RedisOperator.class).getExtension(redisImplType);
				RedisTracer tracer = ExtensionLoader.getExtensionLoader(RedisTracer.class).getExtension(Constant.TRACER_REDIS_CLIENT_NAME);
				redisOperator = DynamicProxyInnerUtil.getProxy(proxy, new TraceHandler<RedisOperator>(tracer));
	     	} else {
	     		redisOperator = ExtensionLoader.getExtensionLoader(RedisOperator.class).getExtension(redisImplType);
	     	}
		}catch(Exception e){
			logger.error(LogConstant.DISTRIBUTE_LOCK + " init redisOperator error", e);
		}
	}

	@Override
	public boolean tryGetDistributedLock(String prefixName, String appName, String lockKey, String _requestId, int timeout, boolean timeOutThenExecBussiness) throws AddLockException {
		String requestId = null;
		// 如果threadlocal里有requestId，则直接使用，否则uuid生成新的
		if(requestIdThreadLocal.get().getRequestId(lockKey) != null){
			requestId = requestIdThreadLocal.get().getRequestId(lockKey);
			if(!LogThreadLocal.restrainLockLog.get()) {
				logger.info(LogConstant.REDIS_LOCK + " old requestId, appName: {}, lockKey: {}, requestId: {}, count: {}, timeout: {}", appName, lockKey, requestId, requestIdThreadLocal.get().getCount(lockKey), timeout);
			}
		}else{
			if(StringUtils.isNotEmpty(_requestId)){
				requestId = _requestId;
				if(!LogThreadLocal.restrainLockLog.get()) {
					logger.info(LogConstant.REDIS_LOCK + " param requestId, appName: {}, lockKey: {}, requestId: {}, count: {}, timeout: {}", appName, lockKey, requestId, 0, timeout);
				}
			}else{
				requestId = UUID.randomUUID().toString();
				if(!LogThreadLocal.restrainLockLog.get()) {
					logger.info(LogConstant.REDIS_LOCK + " new requestId, appName: {}, lockKey: {}, requestId: {}, count: {}, timeout: {}", appName, lockKey, requestId, 0, timeout);
				}
			}
			requestIdThreadLocal.get().setRequestId(lockKey, requestId);
		}
		long start = System.currentTimeMillis();

		/**
    	 * 如果第一个请求执行时间超过了过期时间，在执行过程中锁释放；第二个请求进入并且和第一个请求参数相同；
    	 * 如果此时第一个请求执行完毕并试图删除key，会误删第二个请求的key；
    	 * 所以做了value的验证，需要保证两次请求的value不同
    	 * 如果使用methodName和参数，有可能两次请求的requestId是一样的
    	 */
		// 如果可以直接设置成功锁，就返回1；否则判断是否是自己加的锁，是自己加的锁，则count+1；不是就返回-1
		Object result = null;
		try{
			while(TRY_FALSE.equals(result = redisOperator.setNxAndResetTTL(prefixName, appName, lockKey, requestId, EXPIRE_TIME))) {
	    		// 如果timeOut设置为-1，使没有获取到锁的线程一直处于等待状态，直到获取到锁
	    		if(timeout == -1){
	    			try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						// no-op
					}
	    		}else if((System.currentTimeMillis() - start) < timeout * 1000){
		    		// 超时时间还没到，尝试获取锁
	    			try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						// no-op
					}
		    	}else{
		    		// 超时，返回false，获取锁失败，清除threadlocal
		    		clearThread(lockKey);
		    		if(timeout > 0 && timeOutThenExecBussiness) {
		    			// 如果timeOut大于0，加锁超时后是返回加锁失败（timeOutThenRelease=false）还是执行业务（timeOutThenRelease=true）
		    			return true;
		    		}else {
		    			return false;
		    		}
		    	}
			}
		}catch(Exception e){
			// 加锁异常：有可能是redis服务的问题，为了不阻断服务，认为加锁成功，只是不开启续租，清除threadlocal
			// 注意，因为锁可以重入，有可能第一次加成功了，但第二次失败了，此时必须清除threadlocal
			String msg = LogConstant.REDIS_LOCK + " add redis lock unknow error, appName: " + appName + ", lockKey: " + lockKey + ", requestId: " + requestId;
			logger.error(msg, e);
			// 这里要不要清除threadlocal？
			clearThread(lockKey);
			return true;
		}
		if(result == null){
			// 加锁异常：有可能是redis服务的问题，为了不阻断服务，认为加锁成功，只是不开启续租，清除threadlocal
        	String msg = LogConstant.REDIS_LOCK + " add redis lock result is null, appName: " + appName + ", lockKey: " + lockKey + ", requestId: " + requestId;
        	logger.error(msg);
        	// 这里要不要清除threadlocal？
        	clearThread(lockKey);
        	return true;
        }
		try{
        	Integer.parseInt(result.toString());
        }catch(Exception e){
        	// 加锁异常：返回了一个非数字对象，为了不阻断服务，认为加锁成功，只是不开启续租，清除threadlocal
        	String msg = LogConstant.REDIS_LOCK + " add redis lock result to int error, appName: " + appName + ", lockKey: " + lockKey + ", requestId: " + requestId + ", count: " + result;
        	logger.error(msg, e);
        	// 这里要不要清除threadlocal？
        	clearThread(lockKey);
        	return true;
        }
		int newCount = requestIdThreadLocal.get().addCount(lockKey);
		if(!LogThreadLocal.restrainLockLog.get()) {
			logger.info(LogConstant.REDIS_LOCK + " success, appName: {}, lockKey: {}, requestId: {}, count: {}, timeout: {}", appName, lockKey, requestId, newCount, timeout);
		}
		try {
			//续租
			rerent(prefixName, appName, lockKey);
		}catch (Exception e) {
			logger.info(LogConstant.REDIS_LOCK + " rerent error, appName: {}, lockKey: {}, requestId: {}, count: {}, timeout: {}", appName, lockKey, requestId, newCount, timeout);
		}
		// 能跳出循环，说明已经获取到锁
		return true;
	}

	/**续租
	 * @param appName
	 * @param lockKey
	 */
	private void rerent(String prefixName, String appName, String lockKey) {
		// count：-1加锁失败，不用处理；1第一次加锁成功；>1重入加锁成功
		int count = requestIdThreadLocal.get().getCount(lockKey);
		DelayExecuteMessage<DelayTask> message = threadIdThreadLocal.get().getDelayTask(lockKey);
		if(count == 1) {
			if(message == null){
				preRerent(prefixName, appName, lockKey);
			}
		}else if(count > 1){
			if(message == null){
				// 如果是同线程重入，那么threadlocal数据不会被删除；如果是跨线程重入，threadlocal会是空，需要重新开启续租
				preRerent(prefixName, appName, lockKey);
			}
		}
	}

	/**续租前准备工作
	 * @param appName
	 * @param lockKey
	 */
	private void preRerent(String prefixName, String appName, String lockKey) {
		// 缓存requestId、lockKey，用于延迟任务执行
		DelayExecuteMessage<DelayTask> message = new DelayExecuteMessage<>();
		message.setData(new DelayTask(lockKey, requestIdThreadLocal.get().getRequestId(lockKey), 0, Thread.currentThread()));
		threadIdThreadLocal.get().setDelayTask(lockKey, message);

		// 放入延迟任务，开始DELAY_TIME后续租
		JvmDelayMessageInnerUtil.sendDelayMessage(message, DELAY_TIME, () -> {
			try {
				doRerent(prefixName, appName, message);
			} catch (Exception e) {
				String msg2 = LogConstant.REDIS_LOCK + " start rerent redis lock error, appName: " + appName + ", lockKey: " + lockKey + ", requestId: " + message.getData().getRequestId() + ", message: " + JsonInnerUtil.objecttoJSon(message);
				logger.error(msg2, e);
			}
		});
		if(message != null && message.getData() != null){
			if(!LogThreadLocal.restrainLockLog.get()) {
				logger.info(LogConstant.REDIS_LOCK + " preRerent success, appName: {}, lockKey: {}, requestId: {}, traceId: {}", appName, lockKey, message.getData().getRequestId(), message.getTraceId());
			}
		}
	}

	private void doRerent(String prefixName, String appName, DelayExecuteMessage<DelayTask> message) throws RerentLockException {
		Long traceId = message.getTraceId();
		// 只要traceId不为空，就一直续租，也就是说停止续租的条件是message里的traceId置为空（两种方式：主动置为空；释放锁自动置为空）
		if(traceId != null){
			DelayTask data = message.getData();
			if(data != null){
				if(data.getCount() < MAX_RERENT_TIMES){ // data不为空，且续租次数小于最大续租次数
					data.setCount(data.getCount() + 1);
					Object o = null;
					try{
						o = redisOperator.setNxAndResetTTL(prefixName, appName, data.getLockKey(), data.getRequestId(), EXPIRE_TIME);
					}catch(Exception e){
						// 续租时操作redis时报，则不再续租
						deleteThreadLocal(data.getT(), data.lockKey);
						String msg = LogConstant.REDIS_LOCK + " rerent redis lock error, appName: " + appName + ", lockKey: " + data.getLockKey() + ", requestId: " + data.getRequestId() + ", message: " + JsonInnerUtil.objecttoJSon(message);
						logger.error(msg, e);
						throw new RerentLockException(msg, e);
					}
					// 如果是重入的方式加锁成功，则续租
					// 如果加锁失败不续租
					// 如果是新加锁成功，不续租（这种情况说明在续租的时候锁被释放了，这种情况可能是程序释放完锁，此时还没来得及清空traceId就到了任务执行时间，这时导致新加锁，但只要不开启续租，锁过期后还是会被释放）
					if(Integer.parseInt(String.valueOf(o)) > 1){
						JvmDelayMessageInnerUtil.sendDelayMessage(message, DELAY_TIME, () -> {
							try {
								doRerent(prefixName, appName, message);
							} catch (Exception e) {
								String msg = LogConstant.REDIS_LOCK + " rerent redis lock error, appName: " + appName + ", lockKey: " + data.getLockKey() + ", requestId: " + data.getRequestId() + ", message: " + JsonInnerUtil.objecttoJSon(message);
								logger.error(msg, e);
							}
						});
					}else {
						// 否则，清除锁
						releaseDistributedLock(prefixName, appName, data.lockKey, true, false);
					}
					// 引发续租是一个不太正常的现象，需要关注
					logger.warn(LogConstant.REDIS_LOCK + " rerent redis doRerent, appName: {}, lockKey: {}, requestId: {}, traceId: {}, count: {}", appName, data.getLockKey(), data.getRequestId(), message.getTraceId(), data.getCount());
				}else{
					// 超过了最大续租次数，应该是有问题了
					// 必须清空threadlocal
					deleteThreadLocal(data.getT(), data.lockKey);
					logger.error(LogConstant.REDIS_LOCK + " rerent redis doRerent error: too many, appName: {}, lockKey: {}, requestId: {}, traceId: {}", appName, data.getLockKey(), data.getRequestId(), message.getTraceId());
				}
			}
		}
	}

	/**删除指定线程的threadlocal里的lockKey对应的requestId
	 * @param t
	 * @param lockKey
	 */
	private void deleteThreadLocal(Thread t, String lockKey) {
		try {
			Object threadlocalMap = ReflectionInnerUtil.getFieldValue(t, "threadLocals");
			Object entry = ReflectionInnerUtil.invokeDeclaredMethod(threadlocalMap, "getEntry", new Class[]{ThreadLocal.class}, new Object[]{requestIdThreadLocal});
			RequestIdData value = (RequestIdData)ReflectionInnerUtil.getFieldValue(entry, "value");
			value.removeRequestId(lockKey);
		} catch (Throwable e) {
			logger.error(LogConstant.REDIS_LOCK + " deleteThreadLocal error, lockKey: " + lockKey + ", thread: " + t.getName(), e);
		}
	}

	@Override
	public boolean releaseDistributedLock(String prefixName, String appName, String lockKey, Boolean release, Boolean forceRelease) throws ReleaseLockException, UnrerentLockException {
		if(requestIdThreadLocal.get().getRequestId(lockKey) == null || requestIdThreadLocal.get().getCount(lockKey) == 0){
			return false;
		}

		String requestId = requestIdThreadLocal.get().getRequestId(lockKey);
		int threadCount = requestIdThreadLocal.get().getCount(lockKey);
		if(forceRelease) {
			// 不考虑重入count，强制释放锁
			threadCount = 1;
		}
		// 1、count > 1，则只是减1，不释放锁
		// 2、count == 1 and 设置release为释放锁，则真正释放锁
		if(threadCount > 1 || (threadCount == 1 && release)){
			// 如果是自己加的锁，判断是否count == 1，是就释放锁，返回0；如果count != 1,返回count-1；不是自己加的锁，直接返回-1
			String script = "if redis.call('get', KEYS[1]) == ARGV[1] "
					+ "then if tonumber(ARGV[2]) == 1 "
					+ "then redis.call('del', KEYS[1]); return 0 "
					+ "else return tonumber(ARGV[2]) - 1 "
					+ "end "
					+ "else return -1 "
					+ "end";
			Object result = null;
			try{
				result = redisOperator.eval(prefixName, appName, script, Collections.singletonList(lockKey), Arrays.asList(requestId, String.valueOf(threadCount)));
			} catch(Exception e) {
				// 释放锁时，操作redis失败，
				String msg = LogConstant.REDIS_LOCK + " release redis lock error, appName: " + appName + ", lockKey: " + lockKey + ", requestId: " + requestId + ", count: " + threadCount + ", release: " + release;
				logger.error(msg, e);
				clearThread(lockKey);
				try{
					// 一定要停止续租
					unRerent(lockKey);
				}catch(UnrerentLockException e2){
					String msg2 = LogConstant.REDIS_LOCK + " release redis lock unRerent error, appName: " + appName + ", lockKey: " + lockKey + ", requestId: " + requestId + ", count: " + threadCount + ", release: " + release;
					logger.error(msg2, e2);
					throw new UnrerentLockException(msg2, e2);
				}
				throw new ReleaseLockException(msg, e);
			}
			if(result == null){
				// 释放锁的时候，redis里的key对应的value为空，说明可能是redis异常，或者其他jvm释放了redis锁
				logger.error(LogConstant.REDIS_LOCK + " release redis lock result is null, appName: " + appName + ", lockKey: " + lockKey + ", requestId: " + requestId + ", count: " + threadCount + ", release: " + release);
				clearThread(lockKey);
	        	try{
	        		// 一定要停止续租
	        		unRerent(lockKey);
	        	}catch(UnrerentLockException e2){
					String msg2 = LogConstant.REDIS_LOCK + " release redis lock unRerent error, appName: " + appName + ", lockKey: " + lockKey + ", requestId: " + requestId + ", count: " + threadCount + ", release: " + release;
					logger.error(msg2, e2);
					throw new UnrerentLockException(msg2, e2);
				}
				return true;
			}
			Integer count = null;
			try{
	        	count = Integer.parseInt(result.toString());
	        }catch(Exception e){
	        	String msg = LogConstant.REDIS_LOCK + " release redis lock result to int error, appName: " + appName + ", lockKey: " + lockKey + ", requestId: " + requestId + ", count: " + threadCount + ", release: " + release + ", result: " + result;
	        	logger.error(msg, e);
	        }
	        //redis返回结果不是数字，解锁未知错误，返回false，清除threadlocal
	        if(count == null){
	        	clearThread(lockKey);
	        	try{
	        		// 一定要停止续租
	        		unRerent(lockKey);
	        	}catch(UnrerentLockException e2){
					String msg2 = LogConstant.REDIS_LOCK + " release redis lock unRerent count is null, appName: " + appName + ", lockKey: " + lockKey + ", requestId: " + requestId + ", count: " + threadCount + ", release: " + release;
					logger.error(msg2, e2);
					throw new UnrerentLockException(msg2, e2);
				}
	        	return false;
	        }

	        requestIdThreadLocal.get().setCount(lockKey, count);
	        if (RELEASE_FALSE == count) {
	        	//不是自己加的锁，返回false，清除threadlocal
	        	try{
	        		logger.warn(LogConstant.REDIS_LOCK + " release redis lock not myself get current requestId from redis: {}", StringUtils.defaultIfEmpty(redisOperator.get(prefixName, appName, lockKey), Constant.EMPTY_STRING));
	        	}catch(Exception e){
	        		logger.error(LogConstant.REDIS_LOCK + " release redis lock not myself get current requestId from redis error: ", e);
	        	}
	        	clearThread(lockKey);
	        	logger.warn(LogConstant.REDIS_LOCK + " release redis lock not myself, appName: {}, lockKey: {}, requestId: {}, count: {}, release: {}", appName, lockKey, requestId, count, release);
	        	try{
		        	// 一定要停止续租
		        	unRerent(lockKey);
	        	}catch(UnrerentLockException e2){
					String msg2 = LogConstant.REDIS_LOCK + " release redis lock unRerent not myself error, appName: " + appName + ", lockKey: " + lockKey + ", requestId: " + requestId + ", count: " + count + ", release: " + release;
					logger.error(msg2, e2);
					throw new UnrerentLockException(msg2, e2);
				}
	        	return false;
	        }
	        //System.out.println("lock release count:" + count);
	        //redis返回0，全部解锁，返回true，清除threadlocal
	        if (RELEASE_SUCCESS == count) {
	        	clearThread(lockKey);
	        	try{
		        	// 一定要停止续租
		        	unRerent(lockKey);
	        	}catch(UnrerentLockException e2){
					String msg2 = LogConstant.REDIS_LOCK + " release redis lock unRerent error, appName: " + appName + ", lockKey: " + lockKey + ", requestId: " + requestId + ", count: " + count + ", release: " + release;
					logger.error(msg2, e2);
					throw new UnrerentLockException(msg2, e2);
				}
	        	if(!LogThreadLocal.restrainLockLog.get()) {
	        		logger.info(LogConstant.REDIS_LOCK + " release redis lock success, appName: {}, lockKey: {}, requestId: {}, count: {}, release: {}", appName, lockKey, requestId, count, release);
	        	}
	        	return true;
	        }
	        if(!LogThreadLocal.restrainLockLog.get()) {
	        	logger.info(LogConstant.REDIS_LOCK + " release redis lock reenter success, appName: {}, lockKey: {}, requestId: {}, count: {}, release: {}", appName, lockKey, requestId, count, release);
	        }
	        return true;
		} else if(threadCount == 1 && !release) {
			// 程序执行完成后，不释放锁，这时只需要停止续租
			clearThread(lockKey);
        	try{
				// 一定要停止续租
	        	unRerent(lockKey);
        	}catch(UnrerentLockException e2){
				String msg2 = LogConstant.REDIS_LOCK + " release redis lock only unRerent error, appName: " + appName + ", lockKey: " + lockKey + ", requestId: " + requestId + ", count: " + threadCount + ", release: " + release;
				logger.error(msg2, e2);
				throw new UnrerentLockException(msg2, e2);
			}
        	return true;
		}
        return true;
	}

	private boolean unRerent(String lockKey) throws UnrerentLockException {
		try{
			// 需要清除threadIdThread；或者是手动调用（跨线程加解锁）
			DelayExecuteMessage<DelayTask> message = threadIdThreadLocal.get().getDelayTask(lockKey);
			if(message == null){
				return true;
			}
			if(message.getTraceId() == null){
				return true;
			}
			boolean result = false;
			try{
				result = JvmDelayMessageInnerUtil.cancelTask(message.getTraceId());
			}catch(Exception e){
				// 停止续租主要是靠message.setTraceId(null); message.setData(null);，这里只是停止延迟任务，如果停止失败，也不要影响清空操作
				logger.error(LogConstant.REDIS_LOCK + " unRerent cancelTask error: ", e);
			}
			if(message.getData() != null){
				if(!LogThreadLocal.restrainLockLog.get()) {
					logger.info(LogConstant.REDIS_LOCK + " unRerent success, lockKey: {}, requestId: {}, traceId: {}, result: {}", lockKey, message.getData().getRequestId(), message.getTraceId(), result);
				}
			}
    		message.setTraceId(null);
    		message.setData(null);
    		threadIdThreadLocal.get().removeDelayTask(lockKey);
			return result;
		} catch(Exception e) {
			throw new UnrerentLockException(LogConstant.REDIS_LOCK + " unRerent error: ", e);
		}
	}

	@Override
	public TwoTuple<String, Integer> getLockRequestIdAndCount() {
		return getLockRequestIdAndCount(null);
	}
	
	@Override
	public TwoTuple<String, Integer> getLockRequestIdAndCount(String lockKey) {
		String requestId = requestIdThreadLocal.get().getRequestId(lockKey);
		Integer count = requestIdThreadLocal.get().getCount(lockKey);
		return Tuple.tuple(requestId, count);
	}

	private void clearThread(String lockKey) {
		requestIdThreadLocal.get().removeRequestId(lockKey);
	}
	
	/**requestId对象
	 * @author duoduo
	 *
	 */
	static class RequestIdData implements Serializable {
		private static final long serialVersionUID = 2948336428868329591L;
		private Map<String, RequestIdInnerData> lockKeyMap;
		
		public RequestIdData() {
			super();
			lockKeyMap = new LinkedHashMap<>();
		}
		/**count加1，
		 * @param lockKey
		 * @return 新的count
		 */
		public int addCount(String lockKey) {
			int oldCount = getCount(lockKey);
			int newCount = oldCount + 1;
			setCount(lockKey, newCount);
			return newCount;
		}
		public void removeRequestId(String lockKey) {
			lockKeyMap.remove(lockKey);
		}
		public void setCount(String lockKey, int newCount) {
			RequestIdInnerData inner = lockKeyMap.get(lockKey);
			if(inner == null){
				logger.error(LogConstant.REDIS_LOCK + " setCount error: null, lockKey: " + lockKey + ", newCount: " + newCount);
				return;
			}
			inner.count = newCount;
		}
		public int getCount(String lockKey) {
			RequestIdInnerData inner = null;
			if(lockKey == null){
				inner = MapInnerUtil.getFirstValueOrNull(lockKeyMap);
			}else{
				inner = lockKeyMap.get(lockKey);
			}
			if(inner == null){
				return 0;
			}
			return inner.count;
		}
		public void setRequestId(String lockKey, String requestId) {
			lockKeyMap.put(lockKey, new RequestIdInnerData(requestId));
		}
		public String getRequestId(String lockKey) {
			// 检测异常
			check();
			RequestIdInnerData inner = null;
			if(lockKey == null){
				inner = MapInnerUtil.getFirstValueOrNull(lockKeyMap);
			}else{
				inner = lockKeyMap.get(lockKey);
			}
			if(inner == null){
				return null;
			}
			return inner.requestId;
		}
		private void check() {
			if(lockKeyMap.size() > 3) {
				logger.error(LogConstant.REDIS_LOCK + " lockKeyMap count too larger, thread: " + Thread.currentThread().getName());
				lockKeyMap.entrySet().forEach(data -> {
					RequestIdInnerData requestIdInnerData = data.getValue();
					logger.error(LogConstant.REDIS_LOCK + " lockKeyMap key: " + data.getKey() + " too long, thread: " + Thread.currentThread().getName() + ", requestId: " + Objects.toString(requestIdInnerData.requestId));
				});
				return;
			}
			lockKeyMap.entrySet().forEach(data -> {
				RequestIdInnerData requestIdInnerData = data.getValue();
				if(System.currentTimeMillis() - requestIdInnerData.createTime.getTime() > 5 * 60 * 1000){
					logger.error(LogConstant.REDIS_LOCK + " lockKeyMap time too long, key: " + data.getKey() + ", thread: " + Thread.currentThread().getName() + ", requestId: " + Objects.toString(requestIdInnerData.requestId));
				}
			});
		}
		public Map<String, RequestIdInnerData> getLockKeyMap() {
			return lockKeyMap;
		}
	}
	
	/**requestId对象
	 * @author duoduo
	 *
	 */
	static class RequestIdInnerData implements Serializable {
		private static final long serialVersionUID = -4151228567941000372L;
		public String requestId;
		public int count = 0;
		public Date createTime; // 创建这个threadlocal对象的时间
		
		public RequestIdInnerData(String requestId) {
			this.requestId = requestId;
			this.createTime = new Date();
		}
		@Override
		public String toString() {
			return "RequestIdInnerData [requestId=" + requestId + ", count=" + count + ", createTime=" + createTime + "]";
		}
	}
	
	static class DelayTaskData implements Serializable {
		private static final long serialVersionUID = 6657541907465087671L;
		private Map<String, DelayExecuteMessage<DelayTask>> lockKeyMap;
		
		public DelayTaskData() {
			super();
			lockKeyMap = new HashMap<>();
		}
		public void removeDelayTask(String lockKey) {
			lockKeyMap.remove(lockKey);
		}
		public void setDelayTask(String lockKey, DelayExecuteMessage<DelayTask> delayExecuteMessage) {
			lockKeyMap.put(lockKey, delayExecuteMessage);
		}
		public DelayExecuteMessage<DelayTask> getDelayTask(String lockKey) {
			return lockKeyMap.get(lockKey);
		}
	}

	/**续租对象
	 * @author duoduo
	 *
	 * @param <T>
	 */
	static class DelayTask implements Serializable {
		private static final long serialVersionUID = 6586594134752070186L;
		private String lockKey;
		private String requestId;
		private Thread t; // 为哪个线程续租
		private int count; // 已续租次数

		public DelayTask() {
			super();
		}
		public DelayTask(String lockKey, String requestId, int count, Thread t) {
			super();
			this.lockKey = lockKey;
			this.requestId = requestId;
			this.count = count;
			this.t = t;
		}
		public String getLockKey() {
			return lockKey;
		}
		public String getRequestId() {
			return requestId;
		}
		public int getCount() {
			return count;
		}
		public void setCount(int count) {
			this.count = count;
		}
		public Thread getT() {
			return t;
		}
		
	}

}
